﻿using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Cofe.Core;
using Cofe.Core.Filter;
using Cofe.Core.Logger;

namespace Cofe.Data
{
    public class CofeDBEventListener : IEventLogger<CofeSystemEventArgs>
    {
        [Export(typeof(ICofeService))]
        [ServicePriority(ServicePriorityAttribute.DefaultPriority_COFEDB)]
        public class Factory : IEventListenerFactory
        {
            public void RegisterEventListener(IEventHub eventHub)
            {
                eventHub.RegisterEventListener(new CofeDBEventListener());
            }
        }

        #region Constructor

        #endregion

        #region Methods

        void handleCreateEvent(IEnumerable<IEventLog<CofeSystemEventArgs>> eventsRecorded)
        {
            if (!(eventsRecorded.Any()))
                return;

            var parentPaths = eventsRecorded.Select(eL => PathFE.GetDirectoryName(eL.EventArgs.FullParsePath)).Distinct();
            var parentFcs = from p in parentPaths select FilterCollections.FromParseName(p);

            using (var repository = RepositoryFactory.CreateRepository())
            {
                var entryPHs = from eL in eventsRecorded
                               let entry = EntryFE.TryParsePathAsync(eL.EventArgs.FullParsePath,
                                   ParsableType.ParsePathLookup, EntryConstructionFlags.DefaultWithoutCacheWithLookup).Result
                               where entry != null
                               select entry.Properties;
                repository.AddOrUpdate(false, false, entryPHs.ToArray());

                foreach (var parentFc in parentFcs)
                    repository.Refresh(parentFc, false, true);

                repository.SaveChanges();
            }
        }

        void handleChangeEvent(IEnumerable<IEventLog<CofeSystemEventArgs>> eventsRecorded)
        {
            if (!(eventsRecorded.Any()))
                return;

            List<IEventLog<CofeSystemEventArgs>> passToHandleCreate = new List<IEventLog<CofeSystemEventArgs>>();

            using (var repository = RepositoryFactory.CreateRepository())
            {
                foreach (var el in eventsRecorded)
                {
                    var fc = FilterCollections.FromParseName(el.EventArgs.FullParsePath);
                    if (repository.Exists(fc))
                        repository.Refresh(fc, true, false);
                    else
                        passToHandleCreate.Add(el);

                    repository.SaveChanges();
                }
            }

            handleCreateEvent(passToHandleCreate);
        }

        void handleDeleteEvent(IEnumerable<IEventLog<CofeSystemEventArgs>> eventsRecorded)
        {
            if (!(eventsRecorded.Any()))
                return;

            using (var repository = RepositoryFactory.CreateRepository())
            {
                foreach (var el in eventsRecorded)
                    repository.Remove(el.EventArgs.FullParsePath);
            }
        }

        void handleRenameEvent(IEnumerable<IEventLog<CofeSystemEventArgs>> eventsRecorded)
        {
            if (!(eventsRecorded.Any()))
                return;

            using (var repository = RepositoryFactory.CreateRepository())
            {
                foreach (var el in eventsRecorded)
                    if (el.EventArgs is CofeRenameEventArgs)
                        repository.Remove((el.EventArgs as CofeRenameEventArgs).OldParsePath);
            }
            handleCreateEvent(eventsRecorded);
        }

        public void DispatchEvent(IEnumerable<IEventLog<CofeSystemEventArgs>> eventsRecorded)
        {
            if (!_cancelled)
            {
                try
                {
                    //if (eventsRecorded.Count() > 0)
                    using (var ctx = CofeServices.SecurityManager
                        .ImpersonateAsService(SecurityConstants.ServiceType_EventListener))
                    {

                        using (var r = CofeDBServices.RepositoryFactory.CreateRepository())
                        {
                            r.EventRepository.WriteEvents(eventsRecorded.ToArray());
                            r.SaveChanges();
                        }

                        if (CofeServices.CofeSettings.CacheUpdateOptions.HasFlag(CacheUpdateOptions.passiveUpdate) ||  //Update when changes 
                            CofeServices.CofeSettings.CacheUpdateOptions.HasFlag(CacheUpdateOptions.activeUpdate))  //Update using separate thread.
                        {
                            handleCreateEvent(eventsRecorded.Where(e => e.EventArgs.ChangeType == ModifiedAction.Created));
                            handleChangeEvent(eventsRecorded.Where(e => e.EventArgs.ChangeType == ModifiedAction.Changed
                                //and Create event for same entry not exists.
                                && !(eventsRecorded.Any(e2 => e2.EventArgs.FullParsePath == e.EventArgs.FullParsePath
                                    && e2.EventArgs.ChangeType == ModifiedAction.Created))));
                            handleDeleteEvent(eventsRecorded.Where(e => e.EventArgs.ChangeType == ModifiedAction.Deleted));
                            handleRenameEvent(eventsRecorded.Where(e => e.EventArgs.ChangeType == ModifiedAction.Renamed));
                        }
                    }
                }

                catch (Exception ex)
                {
                    CofeServices.Logger.Log(ex);
                }
            }
        }

        public void Dispose()
        {
            _cancelled = true;
        }

        public IEnumerable<IEventLog<CofeSystemEventArgs>> FIndEvents(EventSearchParam searchParam = null)
        {
            throw new NotImplementedException();


        }

        #endregion

        #region Data

        bool _cancelled = false;
        IRepositoryFactory _repository;


        #endregion

        #region Public Properties

        public CancellationToken CancellationToken { get { return CancellationToken.None; } }
        private IRepositoryFactory RepositoryFactory
        {
            get
            {
                return _repository ?? (_repository =
                    CofeServices.ServiceLocater.FindService<IRepositoryFactory>());
            }
        }

        #endregion






    }
}
